using System;

namespace SeataNet.Core.AT.UndoLog
{
    /// <summary>
    /// Undo日志模型（数据库表映射模型）
    ///
    /// 对应数据库中的undo_log表结构，用于存储和查询回滚日志。
    ///
    /// 数据库表结构（示例）：
    /// ```sql
    /// CREATE TABLE undo_log (
    ///   id BIGINT PRIMARY KEY AUTO_INCREMENT,
    ///   branch_id BIGINT NOT NULL,
    ///   xid VARCHAR(100) NOT NULL,
    ///   context VARCHAR(128),
    ///   rollback_info LONGBLOB NOT NULL,
    ///   log_status INT NOT NULL,
    ///   log_created DATETIME NOT NULL,
    ///   log_modified DATETIME NOT NULL,
    ///   INDEX idx_xid (xid),
    ///   INDEX idx_branch_id (branch_id),
    ///   INDEX idx_log_created (log_created)
    /// );
    /// ```
    ///
    /// 工作流程：
    /// 1. 第一阶段提交时：插入undo_log记录到数据库
    /// 2. 第二阶段提交时：删除undo_log记录
    /// 3. 第二阶段回滚时：查询undo_log记录，生成反向SQL并执行
    ///
    /// 使用场景：
    /// - UndoLogManager写入undo_log到数据库
    /// - UndoLogManager查询undo_log用于回滚
    /// - UndoLogExecutor执行回滚时读取undo_log
    ///
    /// 注意事项：
    /// - RollbackInfo字段存储序列化后的UndoLogRecord（JSON或二进制）
    /// - LogStatus=0表示正常，1表示防御性（防止脏写）
    /// - 需要定期清理已提交事务的undo_log记录
    /// </summary>
    public class UndoLogModel
    {
        /// <summary>
        /// 分支ID
        ///
        /// 分支事务的唯一标识，由TC分配。
        /// 用于关联全局事务中的各个分支。
        /// </summary>
        public long BranchId { get; set; }

        /// <summary>
        /// 全局事务ID
        ///
        /// 标识该undo_log所属的全局事务。
        /// 格式：IP:时间戳:随机数，如"192.168.1.100:1703123456789:5678"
        ///
        /// 用途：
        /// - 回滚时根据XID查询所有相关的undo_log
        /// - 提交时根据XID删除所有undo_log
        /// </summary>
        public string Xid { get; set; }

        /// <summary>
        /// 事务上下文
        ///
        /// 存储额外的上下文信息（可选）。
        ///
        /// 可能包含：
        /// - 应用名称
        /// - 服务名称
        /// - 执行用户
        /// - 其他业务相关信息
        /// </summary>
        public string Context { get; set; }

        /// <summary>
        /// 回滚信息（JSON格式）
        ///
        /// 序列化后的UndoLogRecord对象，包含：
        /// - TableName：表名
        /// - SqlType：SQL类型
        /// - BeforeImage：修改前镜像
        /// - AfterImage：修改后镜像
        ///
        /// 存储格式：
        /// - JSON字符串（推荐）：便于调试和查看
        /// - 二进制（可选）：更紧凑，性能更好
        ///
        /// 用途：
        /// - 回滚时反序列化此字段，获取before/after image
        /// - 根据BeforeImage生成反向SQL
        /// - 根据AfterImage进行脏写检测
        /// </summary>
        public string RollbackInfo { get; set; }

        /// <summary>
        /// 日志状态
        ///
        /// 标识undo_log的当前状态。
        ///
        /// 状态值：
        /// - 0：正常状态，可用于回滚
        /// - 1：防御性状态，用于脏写检测
        ///
        /// 防御性undo_log：
        /// - 当检测到数据被其他事务修改时设置为1
        /// - 回滚时需要人工介入处理
        /// - 避免错误地覆盖其他事务的修改
        /// </summary>
        public int LogStatus { get; set; }

        /// <summary>
        /// 创建时间
        ///
        /// undo_log记录的创建时间（通常是UTC时间）。
        ///
        /// 用途：
        /// - 监控：统计事务执行时长
        /// - 清理：定期删除过期的undo_log
        /// - 审计：记录数据变更的时间点
        /// </summary>
        public DateTime LogCreated { get; set; }

        /// <summary>
        /// 修改时间
        ///
        /// undo_log记录的最后修改时间（通常是UTC时间）。
        ///
        /// 用途：
        /// - 记录状态变更时间（如从正常变为防御性）
        /// - 监控undo_log的处理进度
        /// </summary>
        public DateTime LogModified { get; set; }
    }

    /// <summary>
    /// Undo日志记录
    ///
    /// 内存中的回滚日志结构，包含单个SQL操作的before/after镜像。
    ///
    /// 数据结构：
    /// - TableName：受影响的表名
    /// - SqlType：SQL操作类型（INSERT/UPDATE/DELETE）
    /// - BeforeImage：修改前的数据镜像（TableRecords）
    /// - AfterImage：修改后的数据镜像（TableRecords）
    ///
    /// 工作流程：
    /// 1. AtCommandProxy执行SQL时创建UndoLogRecord
    /// 2. 捕获before image（SQL执行前的数据快照）
    /// 3. 执行业务SQL
    /// 4. 捕获after image（SQL执行后的数据快照）
    /// 5. 将UndoLogRecord序列化后存储到UndoLogModel.RollbackInfo
    /// 6. 提交时写入数据库undo_log表
    ///
    /// 回滚逻辑：
    /// ```
    /// 1. 从数据库查询undo_log记录
    /// 2. 反序列化RollbackInfo为UndoLogRecord
    /// 3. 对比当前数据和AfterImage（脏写检测）
    /// 4. 根据SqlType和BeforeImage生成反向SQL：
    ///    - INSERT -> DELETE
    ///    - UPDATE -> UPDATE（使用BeforeImage的值）
    ///    - DELETE -> INSERT（使用BeforeImage的数据）
    /// 5. 执行反向SQL恢复数据
    /// ```
    ///
    /// 使用示例：
    /// ```csharp
    /// var undoLog = new UndoLogRecord
    /// {
    ///     TableName = "user_table",
    ///     SqlType = SqlType.UPDATE,
    ///     BeforeImage = beforeImageRecords,  // 包含balance=1000的记录
    ///     AfterImage = afterImageRecords     // 包含balance=800的记录
    /// };
    /// // 回滚时生成：UPDATE user_table SET balance=1000 WHERE id=1001
    /// ```
    ///
    /// 注意事项：
    /// - BeforeImage和AfterImage必须包含主键列，用于生成WHERE条件
    /// - 对于INSERT操作，BeforeImage为空
    /// - 对于DELETE操作，AfterImage为空
    /// - UPDATE操作两个镜像都需要
    /// </summary>
    public class UndoLogRecord
    {
        /// <summary>
        /// 表名
        ///
        /// 受SQL影响的数据库表名。
        ///
        /// 用途：
        /// - 回滚时定位要恢复的表
        /// - 生成反向SQL的FROM子句
        /// </summary>
        public string TableName { get; set; }

        /// <summary>
        /// SQL类型
        ///
        /// 标识执行的SQL操作类型。
        ///
        /// 可选值：
        /// - INSERT：插入操作，BeforeImage为空，AfterImage包含新增的数据
        /// - UPDATE：更新操作，BeforeImage和AfterImage都有数据
        /// - DELETE：删除操作，BeforeImage包含删除的数据，AfterImage为空
        /// - SELECT_FOR_UPDATE：读锁，不生成undo_log
        ///
        /// 回滚SQL映射：
        /// - INSERT -> DELETE（删除AfterImage的数据）
        /// - UPDATE -> UPDATE（恢复到BeforeImage的数据）
        /// - DELETE -> INSERT（插入BeforeImage的数据）
        /// </summary>
        public SqlType SqlType { get; set; }

        /// <summary>
        /// 前镜像（修改前的数据）
        ///
        /// SQL执行前的数据快照，用于回滚时恢复数据。
        ///
        /// 内容：
        /// - TableName：表名
        /// - TableMeta：表元数据（主键、列信息）
        /// - Rows：受影响的数据行集合
        ///
        /// 使用场景：
        /// - UPDATE回滚：根据BeforeImage生成SET子句
        /// - DELETE回滚：根据BeforeImage生成INSERT语句
        /// - INSERT操作此字段为null
        ///
        /// 示例（UPDATE user_table SET balance=800 WHERE id=1001）：
        /// BeforeImage包含：{id:1001, balance:1000, name:"Alice"}
        /// </summary>
        public TableRecords BeforeImage { get; set; }

        /// <summary>
        /// 后镜像（修改后的数据）
        ///
        /// SQL执行后的数据快照，用于脏写检测。
        ///
        /// 内容：
        /// - TableName：表名
        /// - TableMeta：表元数据（主键、列信息）
        /// - Rows：受影响的数据行集合
        ///
        /// 使用场景：
        /// - 脏写检测：对比当前数据库的数据和AfterImage
        /// - 如果不一致，说明数据被其他事务修改，需要人工介入
        /// - INSERT回滚：根据AfterImage生成WHERE条件
        /// - DELETE操作此字段为null
        ///
        /// 示例（UPDATE user_table SET balance=800 WHERE id=1001）：
        /// AfterImage包含：{id:1001, balance:800, name:"Alice"}
        /// </summary>
        public TableRecords AfterImage { get; set; }
    }

    /// <summary>
    /// SQL类型枚举
    ///
    /// 定义SQL操作的类型，用于镜像捕获和回滚SQL生成。
    ///
    /// 类型说明：
    /// - INSERT：插入新数据，只需捕获after image
    /// - UPDATE：修改现有数据，需要捕获before和after image
    /// - DELETE：删除现有数据，只需捕获before image
    /// - SELECT_FOR_UPDATE：读锁，不生成undo_log
    ///
    /// 回滚策略：
    /// - INSERT：执行DELETE删除新增的数据
    /// - UPDATE：执行UPDATE恢复到修改前的值
    /// - DELETE：执行INSERT重新插入删除的数据
    /// </summary>
    public enum SqlType
    {
        /// <summary>
        /// INSERT操作
        ///
        /// 特点：
        /// - BeforeImage为空（没有修改前的数据）
        /// - AfterImage包含新增的完整数据
        /// - 回滚：DELETE FROM table WHERE pk=?
        /// </summary>
        INSERT,

        /// <summary>
        /// UPDATE操作
        ///
        /// 特点：
        /// - BeforeImage包含修改前的数据
        /// - AfterImage包含修改后的数据
        /// - 回滚：UPDATE table SET col1=?, col2=? WHERE pk=?
        /// </summary>
        UPDATE,

        /// <summary>
        /// DELETE操作
        ///
        /// 特点：
        /// - BeforeImage包含删除前的完整数据
        /// - AfterImage为空（数据已被删除）
        /// - 回滚：INSERT INTO table (col1, col2) VALUES (?, ?)
        /// </summary>
        DELETE,

        /// <summary>
        /// SELECT FOR UPDATE操作（读锁）
        ///
        /// 特点：
        /// - 不修改数据，只锁定行
        /// - 不生成undo_log
        /// - 不需要镜像捕获
        /// </summary>
        SELECT_FOR_UPDATE
    }

    /// <summary>
    /// 表记录集合
    ///
    /// 存储镜像捕获的表数据，包含表名、表元数据和所有受影响的数据行。
    ///
    /// 数据结构层次：
    /// ```
    /// TableRecords
    /// ├── TableName (string)
    /// ├── TableMeta (TableMeta)
    /// │   ├── TableName (string)
    /// │   ├── PrimaryKeyColumns (List<string>)
    /// │   └── Columns (Dictionary<string, ColumnMeta>)
    /// └── Rows (List<Row>)
    ///     └── Fields (List<Field>)
    ///         ├── Name (string)
    ///         ├── Value (object)
    ///         └── Type (string)
    /// ```
    ///
    /// 使用场景：
    /// - AtCommandProxy.CaptureTableRecordsAsync()返回TableRecords
    /// - UndoLogRecord.BeforeImage和AfterImage使用此类型
    /// - UndoLogExecutor根据TableRecords生成回滚SQL
    ///
    /// 注意事项：
    /// - Rows集合包含所有受影响的数据行（可能是多行）
    /// - 每个Row必须包含主键列的值
    /// - TableMeta包含表结构信息，用于生成SQL
    /// </summary>
    public class TableRecords
    {
        /// <summary>
        /// 表名
        ///
        /// 数据库表的名称，用于生成SQL语句。
        /// </summary>
        public string TableName { get; set; }

        /// <summary>
        /// 表元数据
        ///
        /// 包含表的结构信息：
        /// - 表名
        /// - 主键列名列表
        /// - 所有列的元数据（名称、类型、是否可空等）
        ///
        /// 用途：
        /// - 生成SQL的WHERE子句（使用主键）
        /// - 生成SQL的SET子句（使用列信息）
        /// - 类型转换和验证
        /// </summary>
        public TableMeta TableMeta { get; set; }

        /// <summary>
        /// 记录行集合
        ///
        /// 镜像捕获的所有数据行。
        ///
        /// 特点：
        /// - 可能包含多行（如UPDATE影响了多行）
        /// - 每行包含所有列的字段
        /// - 至少包含主键列的值
        ///
        /// 使用示例：
        /// ```csharp
        /// // UPDATE user_table SET balance=800 WHERE id IN (1001, 1002)
        /// // Rows包含2行：
        /// // Row 1: {id:1001, balance:800, name:"Alice"}
        /// // Row 2: {id:1002, balance:500, name:"Bob"}
        /// ```
        /// </summary>
        public List<Row> Rows { get; set; } = new List<Row>();
    }

    /// <summary>
    /// 表元数据
    ///
    /// 存储数据库表的结构信息，用于SQL生成和数据验证。
    ///
    /// 来源：
    /// - ITableMetaCache.GetTableMetaAsync()查询数据库获取
    /// - 缓存在内存中以提高性能
    /// - 定期刷新以同步表结构变更
    ///
    /// 使用场景：
    /// - 生成before/after image的SELECT语句
    /// - 生成回滚的UPDATE/INSERT/DELETE语句
    /// - 验证数据类型和约束
    /// - 构建WHERE条件（使用主键）
    ///
    /// 注意事项：
    /// - PrimaryKeyColumns不能为空（必须有主键或唯一索引）
    /// - Columns包含所有列的详细信息
    /// - 表结构变更后需要清除缓存
    /// </summary>
    public class TableMeta
    {
        /// <summary>
        /// 表名
        ///
        /// 数据库表的完整名称（可能包含schema）。
        ///
        /// 格式示例：
        /// - MySQL: "user_table" 或 "db.user_table"
        /// - SQL Server: "dbo.user_table" 或 "[dbo].[user_table]"
        /// - PostgreSQL: "public.user_table"
        /// </summary>
        public string TableName { get; set; }

        /// <summary>
        /// 主键列名列表
        ///
        /// 表的主键列名集合，用于生成WHERE条件。
        ///
        /// 特点：
        /// - 单主键表：列表包含1个元素，如["id"]
        /// - 复合主键表：列表包含多个元素，如["user_id", "product_id"]
        /// - 不能为空（AT模式要求表必须有主键）
        ///
        /// 用途：
        /// - 生成WHERE子句：WHERE id=? AND user_id=?
        /// - 唯一标识数据行
        /// - 镜像捕获时定位受影响的行
        /// </summary>
        public List<string> PrimaryKeyColumns { get; set; } = new List<string>();

        /// <summary>
        /// 所有列的元数据字典
        ///
        /// 键：列名（string）
        /// 值：列元数据（ColumnMeta），包含类型、是否可空等信息
        ///
        /// 用途：
        /// - 生成SQL的列列表
        /// - 数据类型验证和转换
        /// - 判断列是否是主键
        /// - 判断列是否可空
        ///
        /// 使用示例：
        /// ```csharp
        /// if (tableMeta.Columns["balance"].IsNullable) {
        ///     // 允许设置为NULL
        /// }
        /// ```
        /// </summary>
        public Dictionary<string, ColumnMeta> Columns { get; set; } = new Dictionary<string, ColumnMeta>();
    }

    /// <summary>
    /// 列元数据
    ///
    /// 存储数据库表列的详细信息。
    ///
    /// 来源：
    /// - 通过查询数据库系统表获取（INFORMATION_SCHEMA.COLUMNS）
    /// - 由ITableMetaCache维护
    ///
    /// 使用场景：
    /// - SQL生成时确定列类型
    /// - 数据验证（类型检查、NULL检查）
    /// - 参数绑定时设置DbType
    ///
    /// 注意事项：
    /// - DataType是数据库特定的类型名称
    /// - 不同数据库的类型名称可能不同（如VARCHAR vs NVARCHAR）
    /// </summary>
    public class ColumnMeta
    {
        /// <summary>
        /// 列名
        ///
        /// 数据库列的名称。
        ///
        /// 注意：
        /// - 可能包含特殊字符，需要转义（如MySQL的反引号）
        /// - 大小写敏感性取决于数据库配置
        /// </summary>
        public string ColumnName { get; set; }

        /// <summary>
        /// 数据类型
        ///
        /// 数据库中的列类型名称。
        ///
        /// 类型示例：
        /// - MySQL: "INT", "VARCHAR", "DATETIME", "DECIMAL"
        /// - SQL Server: "INT", "NVARCHAR", "DATETIME2", "DECIMAL"
        /// - PostgreSQL: "INTEGER", "VARCHAR", "TIMESTAMP", "NUMERIC"
        ///
        /// 用途：
        /// - 生成SQL DDL语句
        /// - 数据类型验证
        /// - 参数绑定时选择合适的DbType
        /// </summary>
        public string DataType { get; set; }

        /// <summary>
        /// 是否主键
        ///
        /// 标识该列是否是表的主键（或复合主键的一部分）。
        ///
        /// 用途：
        /// - 生成WHERE条件时识别主键列
        /// - 镜像捕获时确保包含主键
        /// - 验证数据完整性
        /// </summary>
        public bool IsPrimaryKey { get; set; }

        /// <summary>
        /// 是否可空
        ///
        /// 标识该列是否允许NULL值。
        ///
        /// 用途：
        /// - 数据验证：检查NULL值是否合法
        /// - SQL生成：决定是否需要NULL检查
        /// - 参数绑定：NULL值处理
        ///
        /// 注意：
        /// - 主键列通常不可空
        /// - 可空列在镜像捕获时可能包含NULL值
        /// </summary>
        public bool IsNullable { get; set; }
    }

    /// <summary>
    /// 数据行
    ///
    /// 表示表中的一行数据，包含所有字段的名称、值和类型。
    ///
    /// 数据结构：
    /// - Fields：字段列表，每个字段包含Name、Value、Type
    ///
    /// 特点：
    /// - 灵活的数据结构，支持任意列数
    /// - 不依赖强类型实体类
    /// - 适用于动态表结构
    ///
    /// 使用场景：
    /// - 镜像捕获时存储查询结果
    /// - 回滚时读取数据生成SQL
    ///
    /// 使用示例：
    /// ```csharp
    /// var row = new Row
    /// {
    ///     Fields = new List<Field>
    ///     {
    ///         new Field { Name = "id", Value = 1001, Type = "Int32" },
    ///         new Field { Name = "balance", Value = 1000, Type = "Decimal" },
    ///         new Field { Name = "name", Value = "Alice", Type = "String" }
    ///     }
    /// };
    /// ```
    /// </summary>
    public class Row
    {
        /// <summary>
        /// 字段集合
        ///
        /// 该行包含的所有字段（列）。
        ///
        /// 特点：
        /// - 字段顺序可能与表定义不同
        /// - 包含所有列的值（包括NULL值）
        /// - 至少包含主键列
        ///
        /// 访问方式：
        /// ```csharp
        /// var idField = row.Fields.FirstOrDefault(f => f.Name == "id");
        /// var idValue = idField?.Value;
        /// ```
        /// </summary>
        public List<Field> Fields { get; set; } = new List<Field>();
    }

    /// <summary>
    /// 字段（列值）
    ///
    /// 表示一行中的一个字段，包含字段名、字段值和字段类型。
    ///
    /// 数据结构：
    /// - Name：列名
    /// - Value：列值（可能为null）
    /// - Type：.NET类型名称（如"Int32", "String", "Decimal"）
    ///
    /// 使用场景：
    /// - 镜像捕获时从DbDataReader读取字段
    /// - 回滚时构造SQL参数
    ///
    /// 注意事项：
    /// - Value为null时表示数据库中的NULL值
    /// - Type是.NET类型名称，不是数据库类型名称
    ///
    /// 使用示例：
    /// ```csharp
    /// var field = new Field
    /// {
    ///     Name = "balance",
    ///     Value = 1000.50m,  // decimal类型
    ///     Type = "Decimal"    // .NET类型名称
    /// };
    /// ```
    /// </summary>
    public class Field
    {
        /// <summary>
        /// 字段名（列名）
        ///
        /// 对应数据库表的列名。
        ///
        /// 用途：
        /// - 生成SQL语句的列引用
        /// - 匹配表元数据中的列信息
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 字段值
        ///
        /// 列的实际值，可能为null。
        ///
        /// 类型：
        /// - 基本类型：int, long, string, decimal, DateTime等
        /// - 可空类型：int?, long?, DateTime?等
        /// - null：表示数据库中的NULL值
        ///
        /// 注意：
        /// - 需要根据Type字段进行类型转换
        /// - 回滚时会作为SQL参数绑定
        /// </summary>
        public object Value { get; set; }

        /// <summary>
        /// 字段类型
        ///
        /// .NET类型的名称（Type.Name），用于类型识别和转换。
        ///
        /// 常见类型：
        /// - "Int32"：int类型
        /// - "Int64"：long类型
        /// - "String"：string类型
        /// - "Decimal"：decimal类型
        /// - "DateTime"：DateTime类型
        /// - "Boolean"：bool类型
        ///
        /// 用途：
        /// - 参数绑定时确定DbType
        /// - 数据验证和转换
        /// </summary>
        public string Type { get; set; }
    }
}
